#include "mlvmanager.h"

mlvManager::mlvManager(QObject *parent) :
    QObject(parent)
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
    namesTable.clear();
    relationsTable.clear();
    currentId = 0;
    createSystemArcs();
    createCaseFrameArcs();

    mapPronous.insert(tr("кто"),1);
    mapPronous.insert(tr("что"),2);
    mapPronous.insert(tr("где"),3);
}

int mlvManager::generateId() {
    currentId++;
    return currentId;
}

void mlvManager::createSystemArcs() {
    atom("#имя");
    atom("#объект");
    atom("#агент");
    atom("#место");
    atom("#тема");
    atom("#is_a");
    atom("#is_instance");
}

void mlvManager::createCaseFrameArcs() {
    caseFrameArcs.append(atom("#объект"));
    caseFrameArcs.append(atom("#агент"));
    caseFrameArcs.append(atom("#место"));
    caseFrameArcs.append(atom("#тема"));
}

int mlvManager::getIdByTheName_NamesTable(QString name) {
    elementNamesTable* element = new elementNamesTable();
    foreach(element,namesTable)
        if(element->name == name)
            return element->id;
    return 0;
}

QString mlvManager::getNameByTheId_NamesTable(int id) {
    elementNamesTable* element = new elementNamesTable();
    foreach(element,namesTable)
        if(element->id == id)
            return element->name;
    return "";
}

int mlvManager::getArcByTheNodes(int idNodeStart, int idNodeEnd) {
    elementRelationsTable *element = new elementRelationsTable();
    foreach(element,relationsTable)
        if(element->idA == idNodeStart && element->idB == idNodeEnd)
            return element->idAlph;
    return 0;
}

int mlvManager::getIdNodeByTheNodeAndArc_RelationsTable(int idNode, int idAlpha, int switchNumber) {
    elementRelationsTable *element = new elementRelationsTable();
    switch(switchNumber) {
    case 1:
        //Поиск вершины в которую входит данная дуга из данной вершины
        foreach(element,relationsTable)
            if(element->idA == idNode && element->idAlph == idAlpha)
                return element->idB;
        break;
    case 2:
        //Поиск вершины из которой выходит данная дуга в данную веришну
        foreach(element,relationsTable)
            if(element->idB == idNode && element->idAlph == idAlpha)
                return element->idA;
        break;
    }
    return 0;
}

//Поиск всех вершин которые ссылаются на данную
QList<int> mlvManager::getParentsNode_RelationsTable(int idNode) {
    elementRelationsTable *element = new elementRelationsTable();
    QList<int> listParentsNode;
    foreach(element,relationsTable)
        if(element->idB == idNode)
            listParentsNode.append(element->idA);
    return listParentsNode;
}

//Поиск всех вершин на которые ссылается данная вершина
QList<int> mlvManager::getChildrensNode_RelationsTable(int idNode) {
    elementRelationsTable *element = new elementRelationsTable();
    QList<int> listChildrensNode;
    foreach(element,relationsTable)
        if(element->idA == idNode)
            listChildrensNode.append(element->idB);
    return listChildrensNode;
}

bool mlvManager::checkVerb(QString verb) {
    if(getIdNodeByTheNodeAndArc_RelationsTable(atom(verb),atom("#is_a"),2) != 0)
        return true;
    return false;
}

int mlvManager::checkSubject(QString subject) {
    //Переходим от имени к его сущности
    int idImage = getIdNodeByTheNodeAndArc_RelationsTable(atom(subject),atom("#имя"),2);
    if(idImage == 0)
        return 0;

    //проверяем есть ли дуга is_instance
    if(getIdNodeByTheNodeAndArc_RelationsTable(idImage,atom("#is_instance"),1) != 0)
        return idImage;
    return 0;
}

bool mlvManager::checkEmptyNode(int idNode) {
    if(mota(idNode) == "")
        return true;
    return false;
}

QString mlvManager::convertIdImageToName(int idNode) {
    if(checkEmptyNode(idNode)) {
        int idNameNode = getIdNodeByTheNodeAndArc_RelationsTable(
                    idNode,
                    atom("#имя"),
                    1);
        return mota(idNameNode);
    }
    return mota(idNode);
}

//Функция которая по предметному знанию находи id вершины падежной рамки этого знания
int mlvManager::findImageCaseFrameByTheSubject(int idImageSubject) {
    foreach(int idArc, caseFrameArcs) {
        int idBuffer = getIdNodeByTheNodeAndArc_RelationsTable(idImageSubject,idArc,2);
        if( idBuffer != 0)
            return idBuffer;
    }
    return 0;
}

int mlvManager::findImageBySubject(int idImageSubject) {
    return getIdNodeByTheNodeAndArc_RelationsTable(
                idImageSubject,
                atom("#is_instance"),
                1);
}

//Блок фундоментальных функций
int mlvManager::atom(QString name) {
    int idElement = getIdByTheName_NamesTable(name);
    if(idElement != 0)
        return idElement;
    else {
        elementNamesTable *element = new elementNamesTable();
        element->id = generateId();
        element->name = name;
        namesTable.append(element);
        return element->id;
    }
    return 0;
}

QString mlvManager::mota(int id) {
    QString nameElement = getNameByTheId_NamesTable(id);
    if(nameElement != "")
        return nameElement;
    return nameElement;
}

void mlvManager::setAttr(int idA, int idAlpha, int idB) {
    elementRelationsTable *element = new elementRelationsTable();
    element->idA = idA;
    element->idAlph = idAlpha;
    element->idB = idB;
    relationsTable.append(element);
}

int mlvManager::newNode() {
    return generateId();
}

bool mlvManager::exist(int idA, int idAlpha) {
    if(getIdNodeByTheNodeAndArc_RelationsTable(idA,idAlpha,1) != 0)
        return true;
    return false;
}

//Конец блока Фундоментальных функци

void mlvManager::fillMlvNet(QList<Node *> listNode) {
    relationsTable.clear();
    //Обнуляем все id вершин
    foreach(Node *node, listNode)
        node->resetIdNode();

    //Заполняем таблицу связей
    foreach(Node *node, listNode) {


        QString sourceNodeText = node->toPlainText().trimmed().toLower();
        int idSourceNode = node->getOldIdNode();
        if( idSourceNode == 0) {
            if(sourceNodeText != "")
                idSourceNode = atom(sourceNodeText);
            else
                idSourceNode = newNode();
            node->setIdNode(idSourceNode);
        }

        QList<Edge *> listEdge;
        //Смотрим у вершины исходящии дуги
        listEdge.append(node->edgesFrom(false));
        foreach(Edge *edge,listEdge) {

            int idDestNode = edge->destNode()->getOldIdNode();

            //Если вершина уже добавлена в сеть Млв, переходим к добавлению
            if( idDestNode == 0) {
                //иначе добавляем её, в зависимости от типа(именнованя, неименовананя) соответсвенно
                QString destNodeText = edge->destNode()->toPlainText().trimmed().toLower();
                if(destNodeText != "")
                    idDestNode = atom(destNodeText);
                else
                    idDestNode = newNode();
                edge->destNode()->setIdNode(idDestNode);
            }

            //добавляем исходящии связи
            setAttr(idSourceNode,
                    atom(edge->getTextEdge().trimmed().toLower()),
                    idDestNode);
        }
    }
}
QList<int> mlvManager::getListInstanseNode(int idNode) {
    QList<int> listInstance;
    QList<int> listParentNode = getParentsNode_RelationsTable(idNode);
    foreach(int id,listParentNode) {
        QString nameArc = mota(getArcByTheNodes(id,idNode));
        if(nameArc == "#is_instance")
            listInstance.append(id);
    }
    return listInstance;
}

int mlvManager::getIdFrameByCaseFrame(int idNode, QString pronouns) {
    int idNodeInstance = 0;
    int indexPronous;
    if(!mapPronous.contains(pronouns))
        return 0;
    indexPronous = mapPronous[pronouns];
    switch(indexPronous) {
    case 1:
        idNodeInstance = getIdNodeByTheNodeAndArc_RelationsTable(idNode,
                                                                 atom("#агент"),
                                                                 1);
        break;
    case 2:
        idNodeInstance = getIdNodeByTheNodeAndArc_RelationsTable(idNode,
                                                                 atom("#объект"),
                                                                 1);
        break;
    case 3:
        idNodeInstance = getIdNodeByTheNodeAndArc_RelationsTable(idNode,
                                                                 atom("#место"),
                                                                 1);
        break;
    }

    return idNodeInstance;
}

int mlvManager::idPronounsNode(QString answer, QMap<int, int> mapCaseFrame) {
    answer = answer.trimmed().toLower();
    int indexPronous;
    if(!mapPronous.contains(answer))
        return 0;
    indexPronous = mapPronous[answer];
    switch(indexPronous) {
    case 1:
        if(mapCaseFrame.contains(atom("#агент")))
            return mapCaseFrame[atom("#агент")];
        break;
    case 2:
        if(mapCaseFrame.contains(atom("#объект")))
            return mapCaseFrame[atom("#объект")];
        break;
    case 3:
        if(mapCaseFrame.contains(atom("#место")))
            return mapCaseFrame[atom("#место")];
        break;
    }
    return 0;
}

QString mlvManager::createAnswer(QList<int> listInstance, int idInstanceCaseFrame) {
    QString answer;
    //1 варинт idInstanceCaseFrame == 0 значит множественный ответ
    if(idInstanceCaseFrame == 0)
        foreach(int id, listInstance)
            answer.append(tr(" %1").arg(convertIdImageToName(id)));
    else {
        //2 вариант - у нас есть указатель на определённое предметное знание
        //Если данное предметное знание есть в списке, то это и есть ответ
        //иначе ответа нет
        if(listInstance.indexOf(idInstanceCaseFrame) != -1)
            answer.append(convertIdImageToName(idInstanceCaseFrame));
        else
            answer.append(tr("are no"));
    }
    return answer;
}

//Собираем падежную рамку
QMap<int,int> mlvManager::buildCaseFrame(int idCaseFrame, QString verb, int idImageBySubject) {
    //Получаем все вершины дуги которых выходят из падежной рамки
    QList<int> listOutNode;


    //id названия падежа - id значения падежа
    QMap<int,int> *mapCase = new QMap<int,int>();

    while(idCaseFrame != 0) {
        listOutNode = getChildrensNode_RelationsTable(idCaseFrame);
        //Родитель падежной рамки
        int nextidImageBySubject = 0;
        foreach(int idNode, listOutNode) {
            elementChildren *childNode = new elementChildren();
            childNode->idNode = idNode;
            QString nameArc = mota(getArcByTheNodes(idCaseFrame,idNode));

            if(nameArc == "#is_a" && checkEmptyNode(idNode)) {
                //Проверка наследования
                //Нужно различать is_a между неименованными вершинами - наследование
                //и неименованой с именованой - глагол падежа
                nextidImageBySubject = idNode;
            } else {
                if(!mapCase->contains(atom(nameArc)))
                    mapCase->insert(atom(nameArc),idNode);
            }
        }
        idCaseFrame = nextidImageBySubject;
    }
    return *mapCase;
}

//Метод отвечающий на вопрос пользователя
QString mlvManager::startMlv(QString sentence) {
    QString workSentence = sentence.trimmed().toLower().remove("?");
    QStringList terms = workSentence.split(" ");

    QString noAnswer = tr("are no");
    QString pronouns = "";

    QList<int> listSubjectInSentence;
    QString verbInSentence = "";
    foreach(QString stri, terms) {
        QString str = stri;
        //местоимение
        if(mapPronous.contains(str))
            pronouns = str;
        else {
            int idBuffer = checkSubject(str);
            //Предметное слово
            if( idBuffer != 0)
                listSubjectInSentence.append(idBuffer);
            else
                //Глагол
                if(checkVerb(str))
                    verbInSentence = str;
        }
    }

    if(verbInSentence == "" || listSubjectInSentence.count() == 0)
        return noAnswer;

    //Находим id предметной падежной рамки
    int idSubjectCaseFrame = findImageCaseFrameByTheSubject(
                listSubjectInSentence.first());
    if(idSubjectCaseFrame == 0)
        return noAnswer;

    //Переходим от предметной к декларативной падежной рамки
    int idCaseFrame = getIdNodeByTheNodeAndArc_RelationsTable(
                idSubjectCaseFrame,
                atom("#is_instance"),
                1);
    if(idCaseFrame == 0)
        return noAnswer;

    //Декларативный образ предметного слова
    int idImageBySubject = findImageBySubject(
                listSubjectInSentence.first());

    if(idImageBySubject == 0)
        return noAnswer;

    //Собираем падежную рамку
    //на вход процедуре подаём:id екларативной падежной рамки
    //глагол и декларативный образ предметного слова
    QMap<int,int> mapCaseFrame = buildCaseFrame(idCaseFrame,
                                                verbInSentence,
                                                idImageBySubject);
    //Получаем вершину падежной рамки в которую
    //входит падеж соответсвующего местоимения
    int idNodebyPronouns = idPronounsNode(pronouns,mapCaseFrame);

    //Находим все экземпляры данной вершины
    QList<int> listInstance =  getListInstanseNode(idNodebyPronouns);

    //Находим вершину экземпляр соответсвующего падежа в падежной рамке
    int idInstanceFrame = getIdFrameByCaseFrame(idSubjectCaseFrame,pronouns);
    QString yesAnswer = createAnswer(listInstance,idInstanceFrame);

    if(yesAnswer.trimmed() != "")
        return yesAnswer;

    return noAnswer;
}
